/*
 * author      : dormi330
 * date        : 2018/6/24
 * description : 订单, 按照店铺进行隔离, 拥有一个或多个SKU
 * <p>
 * 一个订单包含多条 item, 但是一次支付
 * 物流可以包含多个
 */

package com.deep.trade.domain.order;

import com.deep.trade.common.BaseEntity;
import com.deep.trade.common.TradeException;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class TradeOrder extends BaseEntity {

    // 常量 - 订单类型
    // public static final String ORDER_TYPE_GENERAL = "general";

    private String orderSummaryId;              // 交易父订单(OrderSummary)id
    private String orderType;                   // 订单类型 目前只支持普通订单 general
    private String channelCode = "pc_web";      // 订单渠道
    private String channelName = "pc_web";      // 订单渠道
    private String code;                        // 订单编码

    private String sellerId;                    // 卖家Id
    private String sellerName;                  // 卖家名称
    private String shopId;                      // 店铺Id
    private String memberId;                    // 会员Id
    private String memberName;                  // 会员名称
    private String remark;                      // 备注
    private String sellerRemark;                // 卖家备注
    private String status;                      // 订单状态, 枚举
    private String preStatus;                   // 前一个状态

    // 与金额相关的信息
    private BigDecimal skuDiscountMoney;        // sku级优惠额度
    private BigDecimal shopDiscountMoney;       // sku级优惠额度
    private BigDecimal platformDiscountMoney;   // 平台优惠
    private BigDecimal freightMoney;            // 订单运费金额小计, 计算值
    private BigDecimal itemMoney;               // 商品金额小计, 计算值
    private BigDecimal payMoney;                // 实付金额, 计算值

    private String auditRejectReason;           // 审核拒绝原因
    private Date auditedAt;                     // 审核时间
    private Date finishedAt;                    // 订单完成时间

    private int plock;                          // 锁, 1=被锁, 0=无锁, 在支付时用到

    public TradeOrder() {
        super();
    }

    /* 非存储字段 */
    private OrderStatusEnum statusEnum;                         // 订单状态, 非存储字段
    private List<TradeOrderSku> skuList = new ArrayList<>();    // 非存储字段
    private DeliveryAddress deliveryAddress;                    // 非存储字段

    public OrderStatusEnum getStatusEnum() {
        return statusEnum;
    }

    public void setStatusEnum(OrderStatusEnum statusEnum) {
        this.preStatus = this.status;
        this.statusEnum = statusEnum;
        this.status = statusEnum.name;
    }

    /** 不要调用此方法, 只留给序列化方法调用 */
    @Deprecated
    public void setStatus(String status) {
        this.status = status;
        this.statusEnum = OrderStatusEnum.fromName(status);
    }

    public DeliveryAddress getDeliveryAddress() {
        return deliveryAddress;
    }

    public void setDeliveryAddress(DeliveryAddress deliveryAddress) {
        deliveryAddress.setTradeOrderId(this.id);
        this.deliveryAddress = deliveryAddress;
    }

    public void addTradeOrderSku(TradeOrderSku sku) {
        sku.setTradeOrderId(this.getId());
        this.skuList.add(sku);
    }

    public TradeOrderSku findSku(String skuId) {
        for (int i = 0; i < skuList.size(); i++) {
            TradeOrderSku sku = skuList.get(i);
            if (sku.getId().equals(skuId)) return sku;
        }
        return null;
    }

    /** 拒绝 申请退款 */
    public boolean rejectRefund() {
        if (statusEnum == OrderStatusEnum.REFUND_APPLIED) {
            rollbackStatus();
            return true;
        } else {
            return false;
        }
    }

    /** 返回上一个状态 */
    private void rollbackStatus() {
        setStatusEnum(OrderStatusEnum.fromName(preStatus));
    }

    /** 申请退款 */
    public boolean applyRefund() {
        if (statusEnum.canChangeTo(OrderStatusEnum.REFUND_APPLIED)) {
            setStatusEnum(OrderStatusEnum.REFUND_APPLIED);
            return true;
        } else {
            return false;
        }
    }

    /** 计算金额, 根据已有信息, 计算推导值 */
    public void calculateMoney() {
        freightMoney = BigDecimal.ZERO;
        itemMoney = BigDecimal.ZERO;
        payMoney = BigDecimal.ZERO;
        skuDiscountMoney = BigDecimal.ZERO;
        shopDiscountMoney = BigDecimal.ZERO;
        platformDiscountMoney = BigDecimal.ZERO;

        for (TradeOrderSku tradeOrderSku : this.skuList) {
            freightMoney = freightMoney.add(tradeOrderSku.getFreightMoney());
            itemMoney = itemMoney.add(tradeOrderSku.getItemMoney());
            payMoney = payMoney.add(tradeOrderSku.getPayMoney());
            skuDiscountMoney = skuDiscountMoney.add(tradeOrderSku.getPayMoney());
            shopDiscountMoney = shopDiscountMoney.add(tradeOrderSku.getPayMoney());
            platformDiscountMoney = platformDiscountMoney.add(tradeOrderSku.getPayMoney());
        }
        // 实付 = 各个sku实付

        /* 如果小于0, 则置为0 */
        payMoney = payMoney.compareTo(BigDecimal.ZERO) == -1 ? BigDecimal.ZERO : payMoney;
    }

    /**
     * 进行审批
     *
     * @param isApprove    是否通过
     * @param rejectReason 拒绝原因
     * @throws TradeException
     */
    public void audit(boolean isApprove, String rejectReason) throws TradeException {
        if (statusEnum.canChangeTo(OrderStatusEnum.AUDIT_REJECTED)
                || statusEnum.canChangeTo(OrderStatusEnum.TO_BE_PAID)) {
            /*
            审批通过 -> 待付款
            拒绝 -> reject
            */
            auditedAt = new Date();
            if (isApprove) {
                setStatusEnum(OrderStatusEnum.TO_BE_PAID);
            } else {
                setStatusEnum(OrderStatusEnum.AUDIT_REJECTED);
                auditRejectReason = rejectReason;
            }
        } else {
            throw new TradeException("cannot audit as status=" + status);
        }
        /* 判断目前状态是否 可被审核 */

    }

    /** 检测订单状态, 是否可以被支付 */
    public boolean canBePaid() {
        return statusEnum == OrderStatusEnum.TO_BE_PAID;
    }

    /** 检测订单状态, 是否可以申请退款 */
    public boolean canRefund() {
        return statusEnum.canChangeTo(OrderStatusEnum.REFUND_APPLIED);
    }

    public boolean cancelRefund() {
        if (statusEnum == OrderStatusEnum.REFUND_APPLIED) {
            rollbackStatus();
            return true;
        } else {
            return false;
        }
    }

    /** 取消订单 */
    public void cancel() throws TradeException {

        if (statusEnum.canChangeTo(OrderStatusEnum.CANNELLED)) {
            setStatusEnum(OrderStatusEnum.CANNELLED);
        } else {
            throw new TradeException("order cannot be cancelled");
        }
    }

    /**
     * 删除订单, src status = cancelled finish
     */
    public void delete() throws TradeException {
        if (statusEnum.canChangeTo(OrderStatusEnum.DELETED)) {
            setStatusEnum(OrderStatusEnum.DELETED);
        } else {
            throw new TradeException("order cannot be cancelled, current status = " + status);
        }
    }

    /**
     * 订单添加包裹。只有 支付完成、投递中 这2个状态, 可以继续添加包裹
     *
     * @return true = 状态改变/false=状态未变
     * @throws TradeException 是否成功
     */
    public boolean parcelAdded() throws TradeException {
        if (statusEnum.canChangeTo(OrderStatusEnum.DELIVERYING)) {
            if (statusEnum == OrderStatusEnum.DELIVERYING) {
                return false;
            } else {
                setStatusEnum(OrderStatusEnum.DELIVERYING);
                return true;
            }
        } else {
            throw new TradeException("cannot add parcel in status =" + status);
        }
    }

    public void increaseVersion() {
        this.version++;
    }

    /** 确认收货=finish */
    public void finish() throws TradeException {
        if (!this.statusEnum.canChangeTo(OrderStatusEnum.FINISHED)) {
            throw new TradeException("order cannot be finish");
        }

        setStatusEnum(OrderStatusEnum.FINISHED);
        this.finishedAt = new Date();
    }

    // ================================================== setter and getter ==================================================

    public String getPreStatus() {
        return preStatus;
    }

    public void setPreStatus(String preStatus) {
        this.preStatus = preStatus;
    }

    public Date getFinishedAt() {
        return finishedAt;
    }

    public void setFinishedAt(Date finishedAt) {
        this.finishedAt = finishedAt;
    }

    public String getOrderSummaryId() {
        return orderSummaryId;
    }

    public void setOrderSummaryId(String orderSummaryId) {
        this.orderSummaryId = orderSummaryId;
    }

    public String getOrderType() {
        return orderType;
    }

    public void setOrderType(String orderType) {
        this.orderType = orderType;
    }

    public String getChannelCode() {
        return channelCode;
    }

    public void setChannelCode(String channelCode) {
        this.channelCode = channelCode;
    }

    public String getChannelName() {
        return channelName;
    }

    public void setChannelName(String channelName) {
        this.channelName = channelName;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getSellerId() {
        return sellerId;
    }

    public void setSellerId(String sellerId) {
        this.sellerId = sellerId;
    }

    public String getSellerName() {
        return sellerName;
    }

    public void setSellerName(String sellerName) {
        this.sellerName = sellerName;
    }

    public String getShopId() {
        return shopId;
    }

    public void setShopId(String shopId) {
        this.shopId = shopId;
    }

    public String getMemberId() {
        return memberId;
    }

    public void setMemberId(String memberId) {
        this.memberId = memberId;
    }

    public String getMemberName() {
        return memberName;
    }

    public void setMemberName(String memberName) {
        this.memberName = memberName;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public String getSellerRemark() {
        return sellerRemark;
    }

    public void setSellerRemark(String sellerRemark) {
        this.sellerRemark = sellerRemark;
    }

    public String getStatus() {
        return status;
    }

    public BigDecimal getSkuDiscountMoney() {
        return skuDiscountMoney;
    }

    public void setSkuDiscountMoney(BigDecimal skuDiscountMoney) {
        this.skuDiscountMoney = skuDiscountMoney;
    }

    public BigDecimal getShopDiscountMoney() {
        return shopDiscountMoney;
    }

    public void setShopDiscountMoney(BigDecimal shopDiscountMoney) {
        this.shopDiscountMoney = shopDiscountMoney;
    }

    public BigDecimal getPlatformDiscountMoney() {
        return platformDiscountMoney;
    }

    public void setPlatformDiscountMoney(BigDecimal platformDiscountMoney) {
        this.platformDiscountMoney = platformDiscountMoney;
    }

    public BigDecimal getFreightMoney() {
        return freightMoney;
    }

    public void setFreightMoney(BigDecimal freightMoney) {
        this.freightMoney = freightMoney;
    }

    public BigDecimal getItemMoney() {
        return itemMoney;
    }

    public void setItemMoney(BigDecimal itemMoney) {
        this.itemMoney = itemMoney;
    }

    public BigDecimal getPayMoney() {
        return payMoney;
    }

    public void setPayMoney(BigDecimal payMoney) {
        this.payMoney = payMoney;
    }

    public String getAuditRejectReason() {
        return auditRejectReason;
    }

    public void setAuditRejectReason(String auditRejectReason) {
        this.auditRejectReason = auditRejectReason;
    }

    public Date getAuditedAt() {
        return auditedAt;
    }

    public void setAuditedAt(Date auditedAt) {
        this.auditedAt = auditedAt;
    }

    public int getPlock() {
        return plock;
    }

    public void setPlock(int plock) {
        this.plock = plock;
    }

    public List<TradeOrderSku> getSkuList() {
        return skuList;
    }

    public void setSkuList(List<TradeOrderSku> skuList) {
        this.skuList = skuList;
    }

}
